/********************************************************************************
 * Copyright(c) 2020-2025 VINCENT_WY All rights reserved.
 * - Filename
 * - Author  Vincent
 * - Version V1.0.0
 * - Date    2023/09/21
 * - Brief
 * - FunctionList:
 ******************************************************************************
 * History:
 *
 *
 *
 ******************************************************************************
 */
#include "ReplayImpl.h"
#include <fstream>
#include "TimeUtil.h"
#include "logutil.h"
#include "FrameWrap.h"

using namespace std;

ReplayImpl::ReplayImpl(const int &id, const std::string &path)
    : Tag("[ReplayImpl" + std::to_string(id) + "]: "), _cameraId(id), _root(path)
{
  LOGD("%s read nv12 file ...", Tag.c_str());
  _fileReaderPtr = std::make_unique<FileReader>(path, true);
  _fileInfoList = _fileReaderPtr->getFileInfoList();
  LOGD("%s Create ...", Tag.c_str());
}

ReplayImpl::~ReplayImpl()
{
  LOGD("%s Destroyed ...", Tag.c_str());
}

void ReplayImpl::start(
    const std::function<void(std::shared_ptr<CameraFrame> &&)> &callback,
    const std::function<void(std::shared_ptr<BevFrame> &&)> & bc,const long long int &time)
{
  if (_fileInfoList.empty())
  {
    LOGE("%s NoFile in list !!!", Tag.c_str());
    return;
  }
  _startTime = time;
  _readLoop = std::make_unique<std::thread>(&ReplayImpl::ReadLoop, this);
  _invokeLoop =
      std::make_unique<std::thread>(&ReplayImpl::InvokeLoop, this, callback, bc);
  _status = Status::START;
}
bool ReplayImpl::isComplete() { return _isComplete; }

long long int ReplayImpl::getFirstFrameTm()
{
  if (_fileInfoList.empty())
    return -1;
  return _fileInfoList[0].timestamp;
}

void ReplayImpl::doRepeat() { _status = Status::REPEAT; }

void ReplayImpl::stop()
{
  LOGD("%s Stop 1...", Tag.c_str());
  _status = Status::IDLE;
  _isExit = true;
  _frameCv.notify_all();
  if ((_readLoop != nullptr) && _readLoop->joinable())
  {
    _readLoop->join();
  }
  if ((_invokeLoop != nullptr) && _invokeLoop->joinable())
  {
    _invokeLoop->join();
  }
  LOGD("%s Stop 2...", Tag.c_str());
}

void ReplayImpl::ReadLoop()
{
  int index = 0;
  long long int mLastTimeStamp = 0;
  while (!_isExit)
  {
    switch (_status)
    {
    case Status::START:
    {
      index = 0;
      _status = Status::CONTINUE;
      mLastTimeStamp = _startTime;
      _isComplete = false;
    }
    break;

    case Status::REPEAT:
    {
      _status = Status::START;
    }
    break;

    case Status::CONTINUE:
    {
      auto t1 = TimeUtil::now_us();
      std::shared_ptr<CameraFrame> frame = std::make_shared<CameraFrame>();
      frame->cameraId = _cameraId;
      frame->width = _fileInfoList[index].width;
      frame->height = _fileInfoList[index].height;
      frame->timestamp.tv_sec = _fileInfoList[index].timestamp / 1000000;
      frame->timestamp.tv_usec = _fileInfoList[index].timestamp % 1000000;
      frame->buffer = _fileInfoList[index].buffer;
      frame->size = frame->width * frame->height * 3 / 2;
      auto t2 = TimeUtil::now_us();
      // 等待两帧之间的时间间隔，时间是us
      std::this_thread::sleep_for(std::chrono::microseconds(
          _fileInfoList[index].timestamp - mLastTimeStamp));
      {
        std::lock_guard<std::mutex> lock(_frameMtx);
        _frameDqu.push_back(frame);
      }
       _frameCv.notify_one();
      auto t3 = TimeUtil::now_us();
     

      // printf("camera id: %d,frame id:%d, file read time wast: %lld us,wait time: %lld us,time period:%lld us \n", _cameraId, index, t2 - t1, t3 - t2, _fileInfoList[index].timestamp - mLastTimeStamp);
      mLastTimeStamp = _fileInfoList[index].timestamp;
      index++;
      if (index >= (int)_fileInfoList.size()-2)
      {
        _status = Status::IDLE;
        _isComplete = true;
        index = 0;
      }
    }
    break;

    case Status::IDLE:
    {
      std::this_thread::sleep_for(std::chrono::milliseconds(3));
    }
    break;

    default:
      break;
    }
  }
}

void ReplayImpl::InvokeLoop(
    const std::function<void(std::shared_ptr<CameraFrame> &&)> &callbak, const std::function<void(std::shared_ptr<BevFrame> &&)> &bc)
{
  while (!_isExit)
  {
    std::unique_lock<std::mutex> lock(_frameMtx);
    _frameCv.wait(lock, [this]
                  { return _frameDqu.size() > 0 || _isExit; });
    if (_isExit)
      break;
    auto frame = _frameDqu.front();
    _frameDqu.pop_front();
    lock.unlock();
    if (_cameraId == FrameWrap::DEVICE_TYPE_BEV) {
        auto bf = make_shared<BevFrame>();
        bf->width = frame->width;
        bf->height = frame->height;
        bf->timestamp = frame->timestamp;
        bf->size = frame->size;
        bf->format = frame->format;
        bf->buffer = std::move(frame->buffer);
        bc(std::move(bf));
    }else {
      callbak(std::move(frame));
    }
  }
}